<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=11"/>
<meta name="generator" content="Doxygen 1.9.4"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>Flow-IPC: Sessions: Opening Channels</title>
<link href="tabs.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="dynsections.js"></script>
<link href="search/search.css" rel="stylesheet" type="text/css"/>
<script type="text/javascript" src="search/searchdata.js"></script>
<script type="text/javascript" src="search/search.js"></script>
<link href="doxygen.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
<div id="titlearea">
<table cellspacing="0" cellpadding="0">
 <tbody>
 <tr id="projectrow">
  <td id="projectalign">
   <div id="projectname">Flow-IPC<span id="projectnumber">&#160;2.0.0</span>
   </div>
   <div id="projectbrief">Flow-IPC project: Full implementation reference.</div>
  </td>
 </tr>
 </tbody>
</table>
</div>
<!-- end header part -->
<!-- Generated by Doxygen 1.9.4 -->
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&amp;dn=expat.txt MIT */
var searchBox = new SearchBox("searchBox", "search",'Search','.html');
/* @license-end */
</script>
<script type="text/javascript" src="menudata.js"></script>
<script type="text/javascript" src="menu.js"></script>
<script type="text/javascript">
/* @license magnet:?xt=urn:btih:d3d9a9a6595521f9666a5e94cc830dab83b65699&amp;dn=expat.txt MIT */
$(function() {
  initMenu('',true,false,'search.php','Search');
  $(document).ready(function() { init_search(); });
});
/* @license-end */
</script>
<div id="main-nav"></div>
<!-- window showing the filter options -->
<div id="MSearchSelectWindow"
     onmouseover="return searchBox.OnSearchSelectShow()"
     onmouseout="return searchBox.OnSearchSelectHide()"
     onkeydown="return searchBox.OnSearchSelectKey(event)">
</div>

<!-- iframe showing the search results (closed by default) -->
<div id="MSearchResultsWindow">
<iframe src="javascript:void(0)" frameborder="0" 
        name="MSearchResults" id="MSearchResults">
</iframe>
</div>

</div><!-- top -->
<div><div class="header">
  <div class="headertitle"><div class="title">Sessions: Opening Channels </div></div>
</div><!--header-->
<div class="contents">
<div class="textblock"><center><b>MANUAL NAVIGATION:</b> <a class="el" href="session_setup.html">Preceding Page</a> - <a class="el" href="session_app_org.html">Next Page</a> - <a href="./pages.html"><b>Table of Contents</b></a> - <a class="el" href="namespaceipc.html"><b>Reference</b></a></center><hr  />
<p >Let's discuss opening channels from within an IPC context (<code>Session</code>). (Or go back to preceding page: <a class="el" href="session_setup.html">Sessions: Setting Up an IPC Context</a>. These two pages are interconnected, as in this context, no pun intended, one uses a <code>Session</code> as a source of channel(s).)</p>
<h2>What's a channel? What channel types are supported by <a class="el" href="namespaceipc_1_1session.html" title="Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...">ipc::session</a>? </h2>
<p >A <b>channel</b> is a <em>bundling</em> of the peer resources required for, essentially, a bidirectional pipe capable of transmitting binary messages (<b>blobs</b>) and, optionally, native handles. A particular <a class="el" href="classipc_1_1transport_1_1Channel.html" title="Peer to a bundle of 1-2 full-duplex pipe(s), one for transmitting unstructured binary blobs; the othe...">ipc::transport::Channel</a> (as specified at compile-time via its template parameters) may, for example, consist of an outgoing POSIX MQ handle and a similar incoming-MQ handle; or conceptually similar SHM-backed MQs (boost.ipc MQs). A peer <a class="el" href="classipc_1_1transport_1_1Channel.html">Channel</a> can be <b>upgraded</b> to an <a class="el" href="classipc_1_1transport_1_1struc_1_1Channel.html" title="Owning and wrapping a pre-connected transport::Channel peer (an endpoint of an established channel ov...">ipc::transport::struc::Channel</a> in order to represent a structured, arbitrarily-sized datum per message as opposed to a mere (limited-size) blob.</p>
<p >A <code>Channel</code> can be assembled manually from its constituent parts via the <a class="el" href="namespaceipc_1_1transport.html" title="Flow-IPC module providing transmission of structured messages and/or low-level blobs (and more) betwe...">ipc::transport</a> API. However this is somewhat painful due to (1) the boiler-plate required and more importantly (2) the naming coordination (and additional cleanup) considerations. It is much easier to do so from within a <code>Session</code> which elides all such details essentially entirely. (That said, regardless of how one came up with it, an open <code>Channel</code> has the same capabilities as another &ndash; certain details omitted. <a class="el" href="namespaceipc_1_1transport.html" title="Flow-IPC module providing transmission of structured messages and/or low-level blobs (and more) betwe...">ipc::transport</a> Manual pages get into all that. In particular though a <code>Channel</code> can always be upgraded into a <code>struc::Channel</code>.)</p>
<p >As of this writing a <em>given</em> <code>Session</code>, on both ends, is capable of opening a single concrete <a class="el" href="classipc_1_1transport_1_1Channel.html" title="Peer to a bundle of 1-2 full-duplex pipe(s), one for transmitting unstructured binary blobs; the othe...">ipc::transport::Channel</a> type. That type is specified at compile time via template arguments to (on session-server side) <code>Session_server</code> and (on client side) <code>Client_session</code>. In <a class="el" href="session_setup.html">Sessions: Setting Up an IPC Context</a> we specified a particular reasonable type, but now let's discuss what capabilities are actually availably fully. Note that once you've made this decision by specifying those particular template arguments on each side, from that point on all required concrete types will be available as aliases, or aliases within that aliased class, or aliases within <em>that</em> aliased class (and so on). For example:</p><ul>
<li>in <a class="el" href="session_setup.html">Sessions: Setting Up an IPC Context</a>, we said <code>using Session_server = Session_server_t&lt;...the_knobs...&gt;; using Session = Session_server::Server_session_obj;</code>;</li>
<li><code>using Channel = Session::Channel_obj;</code> gets us the concrete <code>Channel</code> type.</li>
</ul>
<h3>Specifying a <code>Session</code>'s channel type</h3>
<p >So let's discuss these <code>...the_knobs...</code>. They are the first two template args in:</p>
<div class="fragment"><div class="line"><span class="keyword">template</span>&lt;ipc::session::schema::MqType S_MQ_TYPE_OR_NONE, <span class="keywordtype">bool</span> S_TRANSMIT_NATIVE_HANDLES,</div>
<div class="line">         <span class="keyword">typename</span> Mdt_payload = ::capnp::Void&gt;</div>
</div><!-- fragment --><p >The first question to answer is: Do your channels need to be capable of transmitting <b>native handles</b> (a/k/a FDs in the POSIX world)? This is a fairly specialized need; so in many cases the answer is no. If the answer is yes then, underneath it all, each channel will <em>at least</em> use a bidirectional <b>Unix domain socket, stream-style connection</b>, with <code>epoll_*()</code> as the main signaling mechanism. Local streams are the only relevant transport that can transmit native handles.</p>
<p >The second question to answer is: Do you want to, underneath it all, use <b>message queues (MQs)</b> and if so then what kind? Regardless of the type, enabling MQs in a channel means Flow-IPC shall establish 2 MQs; 1 will be used (exclusively) for A-&gt;B traffic, the other for B-&gt;A. (Each MQ <em>could</em> support an arbitrary number of reader and writer entities, including separate processes, but in this use case each MQ is strictly limited to one reader and one writer, typically separated by a process boundary.)</p><ul>
<li><a href="https://man7.org/linux/man-pages/man7/mq_overview.7.html">POSIX MQ</a>: High-performance OS feature. The authors are not as of this time aware of the technical internal implementation of this technique, but it involves a kernel-persistent (i.e., survives program exit until <code>unlink()</code>ed; does not survive reboot) shared memory area.<ul>
<li>Specify <code>S_MQ_TYPE_OR_NONE = ipc::session::schema::MqType::POSIX</code>.</li>
</ul>
</li>
<li><a href="https://www.boost.org/doc/libs/1_82_0/doc/html/interprocess/synchronization_mechanisms.html#interprocess.synchronization_mechanisms.message_queue">bipc MQ</a>: User-space SHM-backed implementation by boost.interprocess. (Note that the SHM aspect of this is essentially a black box and is not related to <a class="el" href="namespaceipc_1_1shm.html" title="Modules for SHared Memory (SHM) support.">ipc::shm</a>.) Internally, based on its source code, it maintains a ring buffer and/or priority queue; and mutex/condition variables for signaling (particularly when awaiting a message).<ul>
<li>Specify <code>S_MQ_TYPE_OR_NONE = ipc::session::schema::MqType::BIPC</code>.</li>
</ul>
</li>
</ul>
<p >(Otherwise specify <code>S_MQ_TYPE_OR_NONE = ipc::session::schema::MqType::NONE</code>.)</p>
<p >An ipc:session-generated <code>Channel</code> contains <em>either one or two bidirectional pipes</em>; if 2 then they can be used independently of each other, one capable of transmitting blobs, the other of blobs and blob/native-handle pairs. If used directly, sans upgrade to structured messaging, you can use the pipe(s) as desired. Alternatively if a 2-pipe <code>Channel</code> is upgraded to <code>struc::Channel</code> in particular, then it will handle the pipe choice internally on your behalf. For <em>performance</em>:</p><ul>
<li><code>struc::Channel</code> shall use the the blobs-only pipe for any message that does <em>not</em> contain (as specified by the user) a native handle.</li>
<li><code>struc::Channel</code> shall use the the blobs-and-handles pipe for any message <em>does</em> contain a native handle.</li>
<li><code>struc::Channel</code> shall never reorder messages (maintaining, internally, a little reassembly queue in the rare case of a race between a handle-bearing and non-handle-bearing message).</li>
</ul>
<p >All of that said, the bottom line is:</p><ul>
<li>MQ type <code>NONE</code> + <code>S_TRANSMIT_NATIVE_HANDLES=true</code> =&gt; Single pipe: a Unix domain socket stream.</li>
<li>MQ type <code>NONE</code> + <code>S_TRANSMIT_NATIVE_HANDLES=false</code> =&gt; Single pipe: a Unix domain socket stream.</li>
<li>MQ type <code>POSIX</code>/<code>BIPC</code> + <code>S_TRANSMIT_NATIVE_HANDLES=false</code> =&gt; Single pipe: 2 MQs of the specified type, facing opposite directions.</li>
<li>MQ type <code>POSIX</code>/<code>BIPC</code> + <code>S_TRANSMIT_NATIVE_HANDLES=true</code> =&gt; Two pipes:<ul>
<li>2 MQs of the specified type, facing opposite directions (<code>struc::Channel</code> uses for non-handle-bearing messages);</li>
<li>a Unix domain socket stream (<code>struc::Channel</code> uses for handle-bearing messages).</li>
</ul>
</li>
</ul>
<p >This provides a healthy number of choices. However it's best not to overthink it. If native handle transport is required, then you must specify that flag as <code>true</code>. Beyond that it's entirely a question of performance. If, as we recommend, you subsequently use zero-copy (SHM-backed) structured messaging, then we suspect the impact of which knob values you specify is small; the payloads internally being transmitted will always be quite small, and the signaling mechanisms internally used will have similarly small latency profiles. That said it may have some impact. If you intend to use non-zero-copy structured messaging, or unstructured messaging without your own zero-copy mechanics on top, then the impact may be more significant.</p>
<p >A performance analysis at this low level is beyond our scope here (though this may change). We provide now some results from our own tests which should be taken as near-hearsay &ndash; but better than nothing: Using ~2015 server hardware (X4), in Linux, with 256-byte messages (note: larger by ~1 order of magnitude than what zero-copy structured messages use), each of the 3 mechanisms has a raw RTT of ~10 microseconds. Of the three, in decreasing order of perf:</p><ol type="1">
<li>POSIX MQs are fastest (4us).</li>
<li>Unix domain socket streams are close behind (5us).</li>
<li>bipc (SHM-backed ring-buffer) MQs are slowest but still similar (7us).</li>
</ol>
<p >Arguably stream sockets are both versatile (handling handles if needed) and fast, while POSIX MQs are fast, if transmitting FDs is not a factor. Combining both might be best of both worlds but could add a bit of latency due to the more complex logic involved. For you this level of perf analysis can only be settled via your own benchmarks. In the meantime all it takes to change from one to another is, at worst, a recompile &ndash; though, granted, of both applications, which is more complex once they're deployed in the field and require coordinated upgrade.</p>
<h2>Seriously, how to open channels though? </h2>
<ul>
<li><em>Init-channels</em>: There's the synchronous way which is, we feel, preferable when sufficient. Its only deficiency is that it can only be done at the same time as opening a session; therefore the number of channels opened this way has to be decided at that time and no later.</li>
<li><em>On-demand channels</em>: There's also the on-demand/asynchronous way which can be used whenever desired and is thus more flexible. The drawback: by its nature there's an asynchronous step involved which tends to make application logic more complex.</li>
</ul>
<p >Both ways optionally make use of a special technique called <b>channel-open metadata</b>. We will discuss it in more detail below as needed. Essentially it is a <em>small</em> piece of structured data transmitted over the <em>session</em> API around session-open time and/or around subsequent channel-open time; it is a way to communicate information from process A to process B or vice versa intended to help negotiate channel opening and/or identify channels and/or any other basic information transmissible despite no actual channel being available to transmit it. It's a bootstrap mechanism for more complex IPC setups.</p>
<p >That said please remember the following limitation:</p><ul>
<li>A given channel-open metadatum, sent from process A to B, must be capnp-serializable in 1+ buffers of length <a class="el" href="classipc_1_1transport_1_1sync__io_1_1Native__socket__stream.html#a00624e2fc3757012d13e3dc0feb26a21" title="The maximum length of a blob that can be sent by this protocol.">ipc::transport::sync_io::Native_socket_stream::S_MAX_META_BLOB_LENGTH</a> each. As of this writing that value equals 65,534 bytes. So as long as your metadatum is expressible in 1+ such memory segments, it'll work. To not work, your capnp schema must involve a leaf (presumably <code>Text</code>, <code>Data</code>, or <code>List</code>) that exceeds this serialized size at runtime. If not, <a class="el" href="namespaceipc_1_1session.html" title="Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...">ipc::session</a> will emit <a class="el" href="namespaceipc_1_1transport_1_1struc_1_1error.html#a990b9ff850cf9aaf354efdd07d86a71da3deddc950bfa6d35ae03c9494eec15a6" title="Structured message serialization (e.g., when sending over channel): A user-mutated datum (e....">ipc::transport::struc::error::Code::S_INTERNAL_ERROR_SERIALIZE_LEAF_TOO_BIG</a>.</li>
</ul>
<p >In practice this should be more than sufficient for all but the wildest scenarios (in fact exactly 1 such segment should be plenty), as we'll informally show below.</p>
<h3>Opening init-channels</h3>
<p >Simply: you can optionally have N channels available as soon as the session becomes available in opened state (i.e., upon <code>Client_session::sync_connect()</code> sync success and <code>Session_server::async_accept()</code> success + <code>Session_server::init_handlers()</code> sync return). Then you can immediately begin to use them on either side!</p>
<p >The question is, of course: how many is N, and subsequently which one to use for what purpose. Naturally the two sides must agree on both aspects, else chaos ensues. The answer can range from</p><ul>
<li>very simple (N=1!); to</li>
<li>complex (client wants C, server wants S, N=S+C; channel purpose is hard-coded on each side); to</li>
<li>even more complex (same but with channel-purpose information determined at runtime and communicated, once, A-&gt;B and B-&gt;A via <em>channel-open metadata</em>).</li>
</ul>
<p >This requires large amounts of flexibility despite involving zero asynchronicity. What it does <em>not</em> require is back-and-forth negotiation. (If that is truly required, you'll need to consider <a class="el" href="chan_open.html#on_demand">opening on-demand channels</a>.)</p>
<p >So here's how it works in the client-&gt;server direction:</p><ul>
<li>Client side uses 2nd overload of <a class="el" href="classipc_1_1session_1_1Client__session__mv.html#ab971ea979200828d572638c60d7d9f44">Client_session::sync_connect()</a>, specifying how many channels &ndash; call it C &ndash; it wants opened <em>on behalf of the client</em>. C can be zero. Plus, optionally, via the same overload:<ul>
<li>client side specifies via a <b>client-behalf channel-open metadatum</b> Mc any other information it deems worthwhile &ndash; especially having to do with the nature of the C channels it is mandating be init-opened.</li>
</ul>
</li>
<li>As a result:<ul>
<li>C <b>client-behalf init-channels</b> are made available to server side via 2nd overload of <a class="el" href="classipc_1_1session_1_1Session__server.html#a13887cd64912bb5f5675e2817ff8435f">Session_server::async_accept()</a>.<ul>
<li><b>Client-behalf channel-open metadatum</b> Mc is made available, optionally, alongside those C init-channels.</li>
</ul>
</li>
<li>The same C client-behalf init-channels are made available to client side via <code>sync_connect()</code> itself, locally.</li>
</ul>
</li>
</ul>
<p >The server-&gt;client direction is similar but reversed; the data transmitted server-&gt;client being specified via 2nd <a class="el" href="classipc_1_1session_1_1Session__server.html#a13887cd64912bb5f5675e2817ff8435f">Session_server::async_accept()</a> overload and, in summary, comprising:</p><ul>
<li>S <b>server-behalf init-channels</b> and</li>
<li><b>Client-behalf channel-open metadatum</b> Ms.</li>
</ul>
<p >To demonstrate it in code we have to invent a scenario. I choose a moderately complex one involving client-&gt;server only but involving both a non-zero # of init-channels and some metadata. Let's say that the client application determines the number of channels C at runtime in the first <code>main()</code> command-line argument. Further let's say the 2nd command-line argument specifies how many of those C channels shall be structured. (To be clear the latter part is contrived by the Manual author; it can be anything and arbitrarily complex &ndash; as long as it's representable via a capnp schema agreed-upon at compile time by both sides.)</p>
<p >The metadata requires a capnp input file whose resulting capnp-generated .c++ file shall produce an object file compiled into <em>both</em> applications A (server) and B. In our case its contents could be:</p>
<p ><a class="anchor" id="chan_open_mdt_ex"></a> </p><div class="fragment"><div class="line">@0xa780a4869d13f307;</div>
<div class="line">using Cxx = import &quot;/capnp/c++.capnp&quot;;</div>
<div class="line">using Common = import &quot;/ipc/transport/struc/schema/common.capnp&quot;; # Flow-IPC supplies this.</div>
<div class="line">using Size = Common.Size;</div>
<div class="line"> </div>
<div class="line">$Cxx.namespace(&quot;my_meta_app::schema&quot;); # capnp-generated metadata struct matching the below will go into this C++ namespace.</div>
<div class="line"> </div>
<div class="line">struct ChannelOpenMdt</div>
<div class="line">{</div>
<div class="line">  numStructuredChannels @0 :Size;</div>
<div class="line">}</div>
</div><!-- fragment --><p >We are now ready to make the more-complex <code>sync_connect()</code> call. This is a different overload than the one used in the simpler example in <a class="el" href="session_setup.html">Sessions: Setting Up an IPC Context</a>.</p>
<div class="fragment"><div class="line"><span class="keyword">namespace </span>my_meta_app</div>
<div class="line">{</div>
<div class="line">  <span class="comment">// ...</span></div>
<div class="line">  <span class="keyword">using </span>Session = Client_session_t&lt;...knob1..., ...knob2...,</div>
<div class="line">                                   capnp::ChannelOpenMdt&gt;; <span class="comment">// The metadata structure specified compile-time.</span></div>
<div class="line">  <span class="comment">// ...</span></div>
<div class="line"> </div>
<div class="line">  <span class="keyword">auto</span> mdt = session.mdt_builder(); <span class="comment">// This is a shared_ptr&lt;&gt;.</span></div>
<div class="line">  <span class="keyword">auto</span> mdt_root = mdt-&gt;initPayload(); <span class="comment">// This is a my_meta_app::schema::ChannelOpenMdt::Builder.</span></div>
<div class="line">  <span class="keyword">const</span> <span class="keywordtype">size_t</span> num_structured_channels = boost::lexical_cast&lt;size_t&gt;(argv[2]);</div>
<div class="line">  mdt_root.setNumStructuredChannels(num_structured_channels); <span class="comment">// &lt;-- ATTN: runtime config.</span></div>
<div class="line"> </div>
<div class="line">  <span class="comment">// Expected channel-count is specified via the .size() of this vector&lt;Channel&gt;, filled with empty `Channel`s.</span></div>
<div class="line">  Session::Channels init_channels(boost::lexical_cast&lt;size_t&gt;(argv[1])); <span class="comment">// &lt;-- ATTN: runtime config.</span></div>
<div class="line"> </div>
<div class="line">  <a class="code hl_typedef" href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">Error_code</a> err_code;</div>
<div class="line">  session.sync_connect(mdt, <span class="comment">// Client-to-server metadata.</span></div>
<div class="line">                       &amp;init_channels, <span class="comment">// Out-arg for cli-&gt;srv init-channels as well as in-arg for how many we want.</span></div>
<div class="line">                       <span class="keyword">nullptr</span>, <span class="keyword">nullptr</span>, <span class="comment">// In our example we expect no srv-&gt;cli metadata nor srv-&gt;cli init-channels.</span></div>
<div class="line">                       &amp;err_code);</div>
<div class="line">  <span class="keywordflow">if</span> (err_code);</div>
<div class="line">  {</div>
<div class="line">    <span class="comment">// ...</span></div>
<div class="line">    <span class="keywordflow">return</span>;</div>
<div class="line">  }</div>
<div class="line">  <span class="comment">// else:</span></div>
<div class="line"> </div>
<div class="line">  <span class="comment">// `session` is open!</span></div>
<div class="line">  <span class="comment">// `init_channels` are open!  Probably, since we specified num_structured_channels to server, we would</span></div>
<div class="line">  <span class="comment">// now upgrade that many of leading `init_channels` to a struc::Channel each (exercise left to reader).</span></div>
<div class="line"> </div>
<div class="line">  <span class="comment">// Operate on `session` (etc.) in here, long-term, until it is hosed.</span></div>
<div class="line">  go_do_ipc_yay(...);</div>
<div class="line">} <span class="comment">// namespace my_meta_app</span></div>
<div class="ttc" id="anamespaceipc_html_aa3192e586cc45d3e7c22463bf2760f89"><div class="ttname"><a href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">ipc::Error_code</a></div><div class="ttdeci">flow::Error_code Error_code</div><div class="ttdoc">Short-hand for flow::Error_code which is very common.</div><div class="ttdef"><b>Definition:</b> <a href="common_8hpp_source.html#l00298">common.hpp:298</a></div></div>
</div><!-- fragment --><p >On the server end, accordingly:</p>
<div class="fragment"><div class="line"><span class="comment">// ...</span></div>
<div class="line"><span class="keyword">using </span>Session_server = Session_server_t&lt;...knob1..., ...knob2...,</div>
<div class="line">                                        capnp::ChannelOpenMdt&gt;; <span class="comment">// Must match client code above.</span></div>
<div class="line"><span class="comment">// ...</span></div>
<div class="line"> </div>
<div class="line"><span class="comment">// Client-demanded target items.</span></div>
<div class="line">Session_server::Mdt_reader_ptr mdt_for_session_being_opened; <span class="comment">// This, too, is a shared_ptr&lt;&gt;.</span></div>
<div class="line">Session_server::Channels init_channels_for_session_being_opened;</div>
<div class="line"> </div>
<div class="line">session_srv.async_accept(&amp;session_being_opened,</div>
<div class="line">                         <span class="keyword">nullptr</span>, <span class="comment">// In this example we demand no srv-&gt;cli init-channels.</span></div>
<div class="line">                         &amp;mdt_for_session_being_opened, <span class="comment">// We do expect cli-&gt;srv metadata.</span></div>
<div class="line">                         &amp;init_channels_for_session_being_opened,</div>
<div class="line">                         <span class="comment">// Dummy function ignored in our case, because of nullptr in arg 2 above.</span></div>
<div class="line">                         [](<span class="keyword">auto</span>&amp;&amp;...) -&gt; <span class="keywordtype">size_t</span> { <span class="keywordflow">return</span> 0; },</div>
<div class="line">                         <span class="comment">// No-op function, as in our example we don&#39;t transmit srv-&gt;cli metadata.</span></div>
<div class="line">                         [](<span class="keyword">auto</span>&amp;&amp;...) {},</div>
<div class="line">                         ([...](<span class="keyword">const</span> <a class="code hl_typedef" href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">Error_code</a>&amp; err_code)</div>
<div class="line">{</div>
<div class="line">  <span class="comment">// ...same as before...</span></div>
<div class="line">}</div>
<div class="line"> </div>
<div class="line"><span class="keywordtype">void</span> on_new_session(<span class="keyword">const</span> <a class="code hl_typedef" href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">Error_code</a>&amp; err_code)</div>
<div class="line">{</div>
<div class="line">  <span class="keywordflow">if</span> (err_code)</div>
<div class="line">  {</div>
<div class="line">    <span class="comment">// ...</span></div>
<div class="line">    <span class="keywordflow">return</span>;</div>
<div class="line">  }</div>
<div class="line"> </div>
<div class="line">  <span class="comment">// Get the metadata snippet!</span></div>
<div class="line">  <span class="keyword">const</span> <span class="keywordtype">size_t</span> num_structured_channels = mdt_for_session_being_opened-&gt;getPayload().getNumStructuredChannels();</div>
<div class="line">  mdt_for_session_being_opened.reset(); <span class="comment">// Might as well free that RAM.</span></div>
<div class="line"> </div>
<div class="line">  boost::lexical_cast&lt;size_t&gt;(argv[2]);</div>
<div class="line">  <span class="keyword">auto</span> a_session = std::move(session_being_opened);</div>
<div class="line">  <span class="keyword">auto</span> its_init_channels = std::move(init_channels_for_session_being_opened);</div>
<div class="line">  <span class="comment">// session_being_opened and init_channels_for_session_being_opened are now empty again.</span></div>
<div class="line"> </div>
<div class="line">  a_session.init_handlers(...); <span class="comment">// On-error handler (discussed separately).</span></div>
<div class="line"> </div>
<div class="line">  <span class="comment">// `a_session` is open!</span></div>
<div class="line">  <span class="comment">// `its_init_channels` are open!  Probably, since we receivied num_structured_channels from server, we would</span></div>
<div class="line">  <span class="comment">// now upgrade that many of leading `its_init_channels` to a struc::Channel each (exercise left to reader).</span></div>
<div class="line"> </div>
<div class="line">  <span class="comment">// Operate on `a_session` (etc.) in here, long-term, until it is hosed.</span></div>
<div class="line">  go_do_ipc_yay(std::move(a_session), std::move(its_init_channels));</div>
<div class="line"> </div>
<div class="line">  <span class="comment">// ...</span></div>
<div class="line">});</div>
</div><!-- fragment --><p >We leave server-&gt;client channel-count and metadata transmission as an exercise to the reader. It is a bit more complex, because those decisions have to be made <em>during</em> the session-open procedure &ndash; with the opposing-client's identity (<a class="el" href="structipc_1_1session_1_1Client__app.html" title="An App that is used as a client in at least one client-server IPC split.">ipc::session::Client_app</a>) a possible item of interest and only available post-<code>async_accept()</code>-call &ndash; and hence done as part of one or both of the function args to <code>async_accept()</code> (which in our example were dummies/no-ops). We omit it for space.</p>
<p >That said in many cases the real situation will be simpler even than the above. Informally one can think of it as a function call of sorts, in this case the arguments being the channels, the type being the conceptual identity/purpose of each one. Often, in a function call, the number and types of args is known by the caller and the callee at compile time. If not, then more complex measures must be taken.</p>
<p ><a class="anchor" id="on_demand"></a></p><h3>Opening on-demand channels</h3>
<p >The alternative to the preceding technique is an <em>asymmetrical</em> opening of a channel. It is asymmetrical, in that for a <em>given</em> channel, one side must be designated the <b>active-opener</b>, the other the <b>passive-opener</b>. (That said the two <code>Session</code>s are equal in their capabilities, as far as Flow-IPC is concerned: for a given channel, either side can be chosen as the active-opener making the other side the passive-opener as a consequence. It is <em>not</em> relevant which side was chosen as the session-server versus session-client: Either can, at any time, choose to active-open &ndash; as long as the other side accepts passive-opens at all.)</p>
<p >This works as follows:</p><ol type="1">
<li>The passive side, at/before session-open time, sets up a <b>passive-open handler</b> with the signature <code>F(Channel&amp;&amp; new_channel)</code>.</li>
<li>The active side calls <a class="el" href="classipc_1_1session_1_1Session.html#aab01aefa0243abf84c7cc36d3ab2d852">Session::open_channel()</a>. This is a non-blocking, <em>synchronous</em> call. Assuming generally healthy applications, and the presence of the aforementioned opposing passive-open handler, it won't fail.<ul>
<li>On the passive side, when this open-channel request arrives, the handler will be invoked. (The passive-open handler, as of this writing, cannot be unregistered, so if the new channel is unexpected for any reason, it's up to the two applications to deal with it. For example the passive-open side can simply destroy the <code>Channel</code> right away as a way or rejecting it; the opposing side will be informed via the <code>Channel</code> &ndash; not via the <code>Session</code>.)</li>
</ul>
</li>
</ol>
<p >While either side <em>can</em> accept passive-opens, it will only do so if indeed a passive-open handler has been set up on that side. If not, and the opposing side does attempt an active-open (<code>Session::open_channel()</code>), that active-open will quickly fail with a non-session-hosing error, namely <a class="el" href="namespaceipc_1_1session_1_1error.html#a0708d4bec9ae853f4d7e3d7822fc8f2fa2c8c27c65ff6234220a84f96de8a1870" title="Session channel opening: User code chose to reject all passive-open requests.">ipc::session::error::Code::S_SESSION_OPEN_CHANNEL_REMOTE_PEER_REJECTED_PASSIVE_OPEN</a>. Setting up a passive-open is done as follows:</p><ul>
<li>Session-client: It is an argument to the <a class="el" href="namespaceipc_1_1session.html#a77d8fda00e1e17f595a6a5d4c44375e7">Client_session</a> constructor overload. In <a class="el" href="session_setup.html">Sessions: Setting Up an IPC Context</a> we chose, for simplicity of exposition, the other overload which does not take this argument &ndash; meaning we disabled that side as a potential passive-opener, so all opposing <code>open_channel()</code>s would non-fatally-to-session fail. By adding an extra arg in that ctor call, we would have indicated otherwise.</li>
<li>Session-server: It is an argument to the <a class="el" href="classipc_1_1session_1_1Server__session__mv.html#a99097e5aa6883877c74f8a93ebc9f3e1">Server_session::init_handlers()</a> method overload. Again, in our earlier example code we chose the other overload thus disabling server-side passive-opening; by adding an arg we would've indicated otherwise.</li>
</ul>
<p >This may seem asymmetrical, contrary to the spirit of the allegedly-side-agnostic <code>Session</code> concept, but that is not so:</p><ul>
<li><code>Session_client</code> ctor is invoked before the session is "opened," as <code>sync_connect()</code> has not been called let alone succeeded.</li>
<li><code>Session_server::init_handlers()</code> is invoked just before the session is opened, as <code>init_handlers()</code> <em>is</em> the step that promotes the session to "opened" state.</li>
</ul>
<hr  />
<dl class="section user"><dt>Session lifecycle versus Channel lifecycle: They are not linked</dt><dd>Because opening a <code>Channel</code> is so linked with the source <code>Session</code> &ndash; in this context &ndash; it may be natural to assume that they remain somehow linked after that. This is not the case. A <code>Channel</code>, once opened, is independent of any <code>Session</code>. Your application may desire to handle them as a single unit, which we discuss in <a class="el" href="session_app_org.html">Organizing IPC-Related Objects In Your App</a>, but to Flow-IPC they are independent. After all opening the <code>Channel</code> <em>can</em> be done without any <a class="el" href="namespaceipc_1_1session.html" title="Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...">ipc::session</a> involvement; it's "just" easier to use <a class="el" href="namespaceipc_1_1session.html" title="Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...">ipc::session</a>.</dd></dl>
<hr  />
<p >Quick snippets to demonstrate follow. Here we chose the session-client as the passive-open side, just for fun. Client:</p>
<div class="fragment"><div class="line"><span class="comment">// Thread U (probably).</span></div>
<div class="line">Session session(..., ..., ..., ..., <span class="comment">// Logger, Client_app, Server_app, on-error handler.</span></div>
<div class="line">                [...](<a class="code hl_typedef" href="namespaceipc_1_1transport_1_1struc_1_1shm_1_1classic.html#ac543b500a85f556564e592cbbefb6185">Channel</a>&amp;&amp; new_channel,</div>
<div class="line">                      <span class="keyword">auto</span>&amp;&amp;) <span class="comment">// See next Manual section regarding 2nd arg.  In this example we ignore it.</span></div>
<div class="line">{</div>
<div class="line">  <span class="comment">// Technicality: We must post() onto thread U, as usual, but new_channel is a Channel which is move()able but</span></div>
<div class="line">  <span class="comment">// not copyable (which is good, as a Channel is a heavy object, and that would not make sense to copy).</span></div>
<div class="line">  <span class="comment">// Lambda non-ref captures must be, as of C++17 at least, copyable -- even though this copyability</span></div>
<div class="line">  <span class="comment">// usually is not exercised.  A typical technique to overcome this situation is shown here: wrap it in a temporary</span></div>
<div class="line">  <span class="comment">// single-reference shared_ptr.  (unique_ptr would not work, because it is by its nature also not copyable.)</span></div>
<div class="line">  post([..., new_channel_ptr = make_shared&lt;Channel&gt;(std::move(new_channel))]()</div>
<div class="line">  {</div>
<div class="line">    <span class="comment">// Thread U.</span></div>
<div class="line">    <span class="keyword">auto</span> new_channel = std::move(*new_channel_ptr);</div>
<div class="line"> </div>
<div class="line">    <span class="comment">// It&#39;s ready: the other side successfuly invoked session.open_channel().</span></div>
<div class="line">    <span class="comment">// Use it.  E.g., upgrade it to struc::Channel, or not, then send/receive stuff.</span></div>
<div class="line">    go_do_ipc_yay(std::move(new_channel));</div>
<div class="line">  });</div>
<div class="line">});</div>
<div class="ttc" id="anamespaceipc_1_1transport_1_1struc_1_1shm_1_1classic_html_ac543b500a85f556564e592cbbefb6185"><div class="ttname"><a href="namespaceipc_1_1transport_1_1struc_1_1shm_1_1classic.html#ac543b500a85f556564e592cbbefb6185">ipc::transport::struc::shm::classic::Channel</a></div><div class="ttdeci">struc::Channel&lt; Channel_obj, Message_body, Builder::Config, Reader::Config &gt; Channel</div><div class="ttdoc">Convenience alias: Use this when constructing a struc::Channel with tag Channel_base::S_SERIALIZE_VIA...</div><div class="ttdef"><b>Definition:</b> <a href="transport_2struc_2shm_2classic_2classic_8hpp_source.html#l00041">classic.hpp:42</a></div></div>
</div><!-- fragment --><p >And server:</p>
<div class="fragment"><div class="line"><span class="comment">// Thread U.</span></div>
<div class="line"><a class="code hl_typedef" href="namespaceipc_1_1transport_1_1struc_1_1shm_1_1classic.html#ac543b500a85f556564e592cbbefb6185">Channel</a> new_channel;</div>
<div class="line"> </div>
<div class="line"><a class="code hl_typedef" href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">Error_code</a> err_code;</div>
<div class="line">session.open_channel(&amp;new_channel, &amp;err_code); <span class="comment">// Use the non-exception-throwing form, for variety</span></div>
<div class="line"><span class="keywordflow">if</span> (err_code)</div>
<div class="line">{</div>
<div class="line">  <span class="comment">// ...That&#39;s weird!  Some non-session-hosing error occurred; for example</span></div>
<div class="line">  <span class="comment">// ipc::session::error::Code::S_SESSION_OPEN_CHANNEL_REMOTE_PEER_REJECTED_PASSIVE_OPEN would mean the opposing</span></div>
<div class="line">  <span class="comment">// Session didn&#39;t set up a passive-open handler.  Huh....</span></div>
<div class="line">  <span class="comment">// Or ipc::session::error::Code::S_SESSION_OPEN_CHANNEL_SERVER_CANNOT_PROCEED_RESOURCE_UNAVAILABLE indicates</span></div>
<div class="line">  <span class="comment">// some kind of resource (FD count exceeded?  too many MQs in the system versus the limit?) could not be obtained.</span></div>
<div class="line">  <span class="comment">// An error here would usually be a truly exception scenario -- like why isn&#39;t the other side set-up the way</span></div>
<div class="line">  <span class="comment">// the split meta-application is designed?  So maybe trigger the deletion of the entire Session.  However -- up</span></div>
<div class="line">  <span class="comment">// to you to design the protocol; maybe you want it done differently.</span></div>
<div class="line">}</div>
<div class="line"><span class="keywordflow">else</span></div>
<div class="line">{</div>
<div class="line">  <span class="comment">// Use it.  E.g., upgrade it to struc::Channel, or not, then send/receive stuff.</span></div>
<div class="line">  go_do_ipc_yay(std::move(new_channel));</div>
<div class="line">}</div>
</div><!-- fragment --><h3>Opening on-demand channels: Metadata</h3>
<p >We've covered pretty much every approach for opening channels via <a class="el" href="namespaceipc_1_1session.html" title="Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...">ipc::session</a>. Only one topic remains: channel-open may be <em>optionally</em> used when opening a channel on-demand (via <code>Session::open_channel()</code>). Notice the ignored <code>auto&amp;&amp;</code> 2nd arg to the passive-open handler in the <a class="el" href="chan_open.html#on_demand">preceding section</a>? Instead of ignoring it, you can optionally read information (optionally prepared by the active-opener) that might be used to specify how to handle that particular channel. For example, suppose <a class="el" href="chan_open.html#chan_open_mdt_ex">here</a> we had defined in capnp <code>struct ChannelOpenMdt</code> the member <code>doUpgradeToStructuredChannel :Bool</code> which could perhaps instruct the passive-opener as to whether both sides should operate on the channel as a structured channel as opposed to unstructured-binary style. This could look like, on the passive-open side:</p>
<div class="fragment"><div class="line">Session session(..., ..., ..., ..., <span class="comment">// Logger, Client_app, Server_app, on-error handler.</span></div>
<div class="line">                [...](<a class="code hl_typedef" href="namespaceipc_1_1transport_1_1struc_1_1shm_1_1classic.html#ac543b500a85f556564e592cbbefb6185">Channel</a>&amp;&amp; new_channel,</div>
<div class="line">                      Session::Mdt_reader_ptr&amp;&amp; chan_open_mdt)</div>
<div class="line">{</div>
<div class="line">  post([...,</div>
<div class="line">        new_channel_ptr = make_shared&lt;Channel&gt;(std::move(new_channel)),</div>
<div class="line">        chan_open_mdt = std::move(chan_open_mdt)]()</div>
<div class="line">  {</div>
<div class="line">    <span class="keyword">auto</span> new_channel = std::move(*new_channel_ptr);</div>
<div class="line"> </div>
<div class="line">    <span class="comment">// Fish out the metadata.</span></div>
<div class="line">    <span class="keyword">const</span> <span class="keywordtype">bool</span> do_upgrade_to_struct_chan = chan_open_mdt-&gt;getPayload().getDoUpgradeToStructuredChannel();</div>
<div class="line"> </div>
<div class="line">    <span class="comment">// It&#39;s ready: the other side successfuly invoked session.open_channel().</span></div>
<div class="line">    <span class="comment">// Use it.  E.g., upgrade it to struc::Channel, or not -- based on `do_upgrade_to_struct_chan`.</span></div>
<div class="line">    go_do_ipc_yay(std::move(new_channel), do_upgrade_to_struct_chan);</div>
<div class="line">  });</div>
<div class="line">});</div>
</div><!-- fragment --><p >On the active-open side it is accomplished as follows: a different overload of <code>open_channel()</code> also takes a metadata structure, a blank one first being obtained via <code>Session::mdt_builder()</code> and then typically modified with some capnp-generated accessorsno differently fromn the pre-session-open metadata example <a class="el" href="chan_open.html#chan_open_mdt_ex">here.</a></p>
<div class="fragment"><div class="line"><a class="code hl_typedef" href="namespaceipc_1_1transport_1_1struc_1_1shm_1_1classic.html#ac543b500a85f556564e592cbbefb6185">Channel</a> new_channel;</div>
<div class="line"> </div>
<div class="line"><span class="keyword">auto</span> mdt = session.mdt_builder();</div>
<div class="line">mdt-&gt;initPayload().setDoUpgradeToStructuredChannel(<span class="keyword">true</span>); <span class="comment">// Yes!  We do desire to upgrade it to struc::Channel on both sides.</span></div>
<div class="line"> </div>
<div class="line">session.open_channel(&amp;new_channel, mdt);</div>
<div class="line"> </div>
<div class="line"><span class="comment">// Didn&#39;t throw -- use it.  This is jumping ahead into ipc::transport land, so don&#39;t worry if you don&#39;t get it yet;</span></div>
<div class="line"><span class="comment">// but just note we&#39;re acting a certain way on new_channel right away, having informed the passive-open side we</span></div>
<div class="line"><span class="comment">// are expecting it to do the same on its side.</span></div>
<div class="line">Session::Structured_channel&lt;Awesome_structured_capnp_schema_of_the_gods_for_our_cool_channels&gt;</div>
<div class="line">  new_struct_channel(..., std::move(new_channel), ...);</div>
<div class="line"><span class="comment">// ...</span></div>
</div><!-- fragment --><p >In the somewhat advanced situation where one wants to use both methods of channel-opening (init-channels and on-demand), or some other complex scenario, recall that this is capnp; you can use its <code>union</code>s (anonymous or otherwise) and so on. There's no performance penalty; both applications just need to agree on the protocol at compile-time. (If you're comfortable with Cap'n Proto, this isn't news to you.) For example:</p>
<div class="fragment"><div class="line">struct ChannelOpenMdt</div>
<div class="line">{</div>
<div class="line">  struct InitChannelCfg</div>
<div class="line">  {</div>
<div class="line">    numStructuredChannels @0 :Size;</div>
<div class="line">    # Futher stuff... needn&#39;t just be scalars either.</div>
<div class="line">  }</div>
<div class="line">  struct OnDemandChannelCfg</div>
<div class="line">  {</div>
<div class="line">    doUpgradeToStructuredChannel @0 :Bool;</div>
<div class="line">    # Stuff....</div>
<div class="line">  }</div>
<div class="line"> </div>
<div class="line">  union</div>
<div class="line">  {</div>
<div class="line">    init @0 :InitChannelCfg;</div>
<div class="line">    onDemand @1 :OnDemandChannelCfg;</div>
<div class="line">  }</div>
<div class="line">}</div>
</div><!-- fragment --><div class="fragment"><div class="line"><span class="comment">// One type used for both purposes: ChannelOpenMdt:</span></div>
<div class="line"><span class="keyword">using </span>Session_server = Session_server_t&lt;...knob1..., ...knob2...,</div>
<div class="line">                                        capnp::ChannelOpenMdt&gt;;</div>
<div class="line"> </div>
<div class="line"><span class="comment">// When accepting a new session, init-channel-open metadata:</span></div>
<div class="line"><span class="keywordtype">void</span> on_new_session(<span class="keyword">const</span> <a class="code hl_typedef" href="namespaceipc.html#aa3192e586cc45d3e7c22463bf2760f89">Error_code</a>&amp; err_code)</div>
<div class="line">{</div>
<div class="line">  <span class="comment">// ...</span></div>
<div class="line">  <span class="comment">// Get the metadata snippet!</span></div>
<div class="line">  <span class="keyword">const</span> <span class="keywordtype">size_t</span> num_structured_channels</div>
<div class="line">    = mdt_for_session_being_opened-&gt;getPayload().getInit().getNumStructuredChannels();</div>
<div class="line">  <span class="comment">// On opposing side, we would&#39;ve done: initPayload().initInit().setNumStructuredChannels().</span></div>
<div class="line">  <span class="comment">// ...</span></div>
<div class="line">}</div>
<div class="line"> </div>
<div class="line"><span class="comment">// When accepting a channel as passive-opened, on-demand-channel-open metadata:</span></div>
<div class="line"><span class="keywordtype">void</span> on_new_channel(<a class="code hl_typedef" href="namespaceipc_1_1transport_1_1struc_1_1shm_1_1classic.html#ac543b500a85f556564e592cbbefb6185">Channel</a>&amp;&amp; new_channel, Session::Mdt_reader_ptr&amp;&amp; chan_open_mdt)</div>
<div class="line">{</div>
<div class="line">  <span class="comment">// ...</span></div>
<div class="line">  <span class="comment">// Get the metadata snippet!</span></div>
<div class="line">  <span class="keyword">const</span> <span class="keywordtype">bool</span> do_upgrade_to_struct_chan = chan_open_mdt-&gt;getPayload().getOnDemand().getDoUpgradeToStructuredChannel(...).</div>
<div class="line">  <span class="comment">// On opposing side, we would&#39;ve done: initPayload().initOnDemand().setDoUpgradeToStructuredChannel(...).</span></div>
<div class="line">  <span class="comment">// ...</span></div>
<div class="line">}</div>
</div><!-- fragment --><p >Opening channels is the main point of sessions, so we are close to done, as far as mainstream <a class="el" href="namespaceipc_1_1session.html" title="Flow-IPC module providing the broad lifecycle and shared-resource organization – via the session conc...">ipc::session</a> topics go. One basic thing we did skip over is error handling; which is related to the topic of the best way to organize the IPC-relevant parts of an application. Let's <a class="el" href="session_app_org.html">discuss that next</a>.</p>
<hr  />
<center><b>MANUAL NAVIGATION:</b> <a class="el" href="session_setup.html">Preceding Page</a> - <a class="el" href="session_app_org.html">Next Page</a> - <a href="./pages.html"><b>Table of Contents</b></a> - <a class="el" href="namespaceipc.html"><b>Reference</b></a></center> </div></div><!-- contents -->
</div><!-- PageDoc -->
<!-- start footer part -->
<hr class="footer"/><address class="footer"><small>
Generated on Fri Apr 11 2025 20:02:26 for Flow-IPC by&#160;<a href="https://www.doxygen.org/index.html"><img class="footer" src="doxygen.svg" width="104" height="31" alt="doxygen"/></a> 1.9.4
</small></address>
</body>
</html>
